﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using EnvDTE;
using EnvDTE80;
using XamlLocalizer.Services;

namespace XamlLocalizerAddin.ViewModels
{
    public class VisualStudioAdapter : IVisualStudioAdapter
    {
        private const string SilverlightExtenderName = "SilverlightProject";

        private readonly DTE2 applicationObject;

        public VisualStudioAdapter(DTE2 applicationObject)
        {
            this.applicationObject = applicationObject;
            ValidateCompatibility();
        }

        public string GetSolutionFolder()
        {
            string solutionFile = applicationObject.Solution.FullName;
            return Path.GetDirectoryName(solutionFile);
        }

        public string GetActiveDocumentName()
        {
            string viewName = Path.GetFileNameWithoutExtension(applicationObject.ActiveDocument.Name);
            return viewName;
        }

        public string GetActiveDocumentText()
        {
            return GetDocumentText(applicationObject.ActiveDocument);
        }

        public void SetActiveDocumentText(string newText)
        {
            SetDocumentText(applicationObject.ActiveDocument, newText);
        }

        public string GetActiveAssemblyName()
        {
            Project project = applicationObject.ActiveDocument.ProjectItem.ContainingProject;
            var assemblyName = project.Properties.Item("AssemblyName").Value.ToString();
            //var outputType = project.Properties.Item("OutputType").Value.ToString();
            //var fullAssemblyName = assemblyName + outputType == "WinExe" ? ".exe" : ".dll";
            return assemblyName;
        }

        private static string GetDocumentText(Document document)
        {
            if (document == null)
            {
                throw new ArgumentNullException("document");
            }
            var textDoc = (TextDocument)document.Object(null);
            return textDoc.StartPoint.CreateEditPoint().GetText(textDoc.EndPoint.AbsoluteCharOffset);
        }

        private static void SetDocumentText(Document document, string newText)
        {
            if (document == null)
            {
                throw new ArgumentNullException("document");
            }
            var textDoc = (TextDocument)document.Object(null);
            textDoc.StartPoint.CreateEditPoint().
                ReplaceText(textDoc.EndPoint.AbsoluteCharOffset, newText, 0);
        }

        public IEnumerable<string> GetAllXamlFiles(bool lookOnlyInCurrentProject)
        {
            var res = new List<string>();

            IEnumerable projects;
            if (!lookOnlyInCurrentProject)
            {
                projects = GetProjects();
            }
            else
            {
                Project project = applicationObject.ActiveDocument.ProjectItem.ContainingProject;
                projects = new ArrayList { project };
            }

            foreach (Project project in projects)
            {
                var projectItems = project.ProjectItems;
                IterateProjectItems(res, projectItems);
            }

            return res;
        }

        private IEnumerable GetProjects()
        {
            var result = new ArrayList();
            foreach (Project project in applicationObject.Solution.Projects)
            {
                if (project.Kind == Constants.vsProjectKindMisc) continue;

                if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
                {
                    AddSolutionFolderProjects(project, result);
                }
                else
                {
                    result.Add(project);
                }
            }
            return result;
        }

        private static void AddSolutionFolderProjects(Project solutionFolder, ArrayList result)
        {
            foreach (ProjectItem projectItem in solutionFolder.ProjectItems)
            {
                Project subProject = projectItem.SubProject;
                if (subProject == null)
                {
                    continue;
                }

                // If this is another solution folder, do a recursive call, otherwise add
                if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder)
                {
                    AddSolutionFolderProjects(subProject, result);
                }
                else
                {
                    result.Add(subProject);
                }
            }
        }

        private void IterateProjectItems(List<string> res, ProjectItems projectItems)
        {
            foreach (ProjectItem projectItem in projectItems)
            {
                string fileName = projectItem.FileNames[1];

                if (IsXaml(fileName))
                {
                    res.Add(fileName);
                }
                else
                {
                    if (Directory.Exists(fileName))
                    {
                        IterateProjectItems(res, projectItem.ProjectItems);
                    }
                }
            }
        }

        private bool IsXaml(string fileName)
        {
            var ext = Path.GetExtension(fileName);
            if (String.Compare(ext, ".xaml", StringComparison.OrdinalIgnoreCase) == 0)
            {
                return true;
            }
            return false;
        }

        public bool IsActiveProjectSilverlight()
        {
            Project project = applicationObject.ActiveDocument.ProjectItem.ContainingProject;
            return ProjectHasExtender(project, SilverlightExtenderName);
        }

        public bool ProjectHasExtender(Project proj, string extenderName)
        {
            bool result = false;
            object[] extenderNames;

            try
            {
                // We could use proj.Extender(extenderName) but it causes an exception if not present and 
                // therefore it can cause performance problems if called multiple times. We use instead:

                extenderNames = (object[])proj.ExtenderNames;

                if (extenderNames.Length > 0)
                {
                    foreach (object extenderNameObject in extenderNames)
                    {
                        if (extenderNameObject.ToString() == extenderName)
                        {
                            result = true;
                            break;
                        }
                    }
                }
            }
            catch
            {
                // Ignore
            }
            return result;
        }

        private void ValidateCompatibility()
        {
            if (!ActiveDocumentIsInProject())
            {
                throw new NotSupportedException("XamlLocalizer can work only with files, included in current solution project.");
            }
        }

        private bool ActiveDocumentIsInProject()
        {
            var projectItem = applicationObject.ActiveDocument.ProjectItem;
            return (projectItem != null && projectItem.ContainingProject != null &&
                    projectItem.ContainingProject.UniqueName != Constants.vsMiscFilesProjectUniqueName);
        }
    }
}
